#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/rand.h>

void handle_errors() {
    fprintf(stderr, "An error occurred in OpenSSL\n");
}

// 打印16进制数据
void print_hex(const char* label, const unsigned char* data, int len) {
    printf("%s (%d bytes):\n", label, len);
    for (int i = 0; i < len; ++i) {
        printf("%02x", data[i]);
    }
    printf("\n");
}

int main() {
    unsigned char key[16]; // SM4 密钥 16 字节
    unsigned char iv[16];  // SM4 IV 16 字节

    // 1. 生成随机密钥和IV
    if (RAND_bytes(key, sizeof(key)) != 1 || RAND_bytes(iv, sizeof(iv)) != 1) {
        handle_errors();
        return 1;
    }

    print_hex("SM4 Key", key, sizeof(key));
    print_hex("SM4 IV", iv, sizeof(iv));

    const unsigned char plaintext[] = "This is a test message for SM4 with OpenSSL";
    unsigned char ciphertext[128];
    unsigned char decryptedtext[128];
    int len, ciphertext_len, decryptedtext_len;

    EVP_CIPHER_CTX *ctx;

    // 2. 加密
    if (!(ctx = EVP_CIPHER_CTX_new())) handle_errors();

    // 使用 EVP_sm4_cbc()
    if (1 != EVP_EncryptInit_ex(ctx, EVP_sm4_cbc(), NULL, key, iv)) handle_errors();

    if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, strlen((char*)plaintext))) handle_errors();
    ciphertext_len = len;

    if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handle_errors();
    ciphertext_len += len;

    EVP_CIPHER_CTX_free(ctx);
    print_hex("Ciphertext", ciphertext, ciphertext_len);

    // 3. 解密
    if (!(ctx = EVP_CIPHER_CTX_new())) handle_errors();

    if (1 != EVP_DecryptInit_ex(ctx, EVP_sm4_cbc(), NULL, key, iv)) handle_errors();

    if (1 != EVP_DecryptUpdate(ctx, decryptedtext, &len, ciphertext, ciphertext_len)) handle_errors();
    decryptedtext_len = len;

    if (1 != EVP_DecryptFinal_ex(ctx, decryptedtext + len, &len)) handle_errors();
    decryptedtext_len += len;

    EVP_CIPHER_CTX_free(ctx);

    decryptedtext[decryptedtext_len] = '\0'; // 添加字符串结束符
    printf("Decrypted text: %s\n", (char*)decryptedtext);

    // 验证
    if (strcmp((char*)plaintext, (char*)decryptedtext) == 0) {
        printf("SM4 Enc/Dec Success!\n");
    } else {
        printf("SM4 Enc/Dec Failed!\n");
    }

    return 0;
}
